BlogAboutGuestBook
인스타그램 주소
HJ DevLog
©2025 효중킴의 블로그, Powered By Next.js

유틸리티 타입 정리

유틸리티 타입에 대해 알아보자

2023-10-22

마크다운 이미지

유틸리티 타입은 정의해 놓은 타입을 변환할 때 사용하기 좋은 TypeScript가 제공하는 도구이다. 유틸리티 타입을 쓰지 않더라도 기본 문법으로 타입을 변환할 수 있지만 유틸리티 타입을 사용하면 좀 더 간결하게 타입을 변환할 수 있다.

Partial

Type집합의 모든 프로퍼티를 선택적으로 타입을 변환하는 유틸리티 타입이다.

이 때 Partial을 사용하면 Book의 모든 프로퍼티를 선택적으로 바꿔줌으로서 에러를 잡을 수 있다.

14.2 Required

Required은 Partial과 반대의 개념이다. Partial이 Type 집합의 모든 프로퍼티를 선택적으로 타입을 변환하는 유틸리티 타입이라면, Required은 Type 집합의 모든 프로퍼티를 필수로 설정한 타입을 생성하는 유틸리티 타입이다.

하지만 Required 유틸리티 타입을 사용한다면 모든 프로퍼티를 필수로 바꾸기 때문에 에러가 뜬다.

14.3 Readonly

Type의 모든 프로퍼티를 재할당이 불가능한 읽기 전용(Readonly) 으로 설정한 타입을 생성한다.

읽기 전용(Readonly)가 아니면 재할당이 가능하다.

하지만 Readonly을 준다면 모든 프로퍼티가 읽기 전용(Readonly) 이 되면서 재할당이 불가능하게 된다.

14.4 Record<Keys, Type>

프로퍼티 타입을 Key로, value 타입을 Type으로 지정해 생성한다. Record<Keys,Type> 유틸리티 타입은 프로퍼티를 다른 타입으로 매핑하고 싶을 때 사용하면 좋다.

value 값의 반복이 보인다. 이를 Record<Keys,Type>을 활용하면 해결할 수 있다.

훨씬 간결해진 모습이지만 조금 길어 보인다. 이를 type을 활용해 정리해 줄 수 있다.

type을 활용해 정리해 주면 재사용에 용이하다.

Pick<Type, Keys>

Type에서 프로퍼티 Keys를 Pick(선택)해 타입을 생성한다.

아래 예제는 Person에서 name과 age를 선택해 kim을 생성한다.

만약 name과 age가 아닌 다른 key를 선택하게 되면 에러가 발생한다.

Pick<Type,Keys> 유틸리티 역시 type을 사용해 정리할 수 있다.

Omit<Type, Keys>

Pick 유틸리티 타입과는 반대 개념으로 Type에서 프로퍼티 Keys를 Omit(생략)해 타입을 생성한다.

아래 예제는 Person에서 age와 gender를 생략해 kim을 생성한다.

만약 age나 gender를 포함하게 되면 에러가 발생한다.

Omit<Type,Keys> 유틸리티 역시 type을 사용해 정리할 수 있다.

Exclude<Type, ExcludeUnion>

ExcludeUnion에 들어갈 수 있는 모든 유니온을 Type에서 제외한 타입을 생성한다.

type T1에서 "park" 을 제외한 "kim" | "lee"가 남게 된다. type T2 역시 number와 boolean 을 제외한 string이 남게 된다.

14.6 NonNullable

Type에서 null과 undefined를 제외하고 타입을 생성한다.

T1 에서 null과 undefined을 제외한 string과 number, boolean이 남게 된다.

컴포넌트 작성에 대한 고민들 (합성 컴포넌트)

뒤죽박죽 코드블록 컴포넌트 뜯어 고치기

  • Partial
  • 14.2 Required
  • 14.3 Readonly
  • 14.4 Record<Keys, Type>
  • Pick<Type, Keys>
  • Omit<Type, Keys>
  • Exclude<Type, ExcludeUnion>
  • 14.6 NonNullable<Type>
interface Book {
  title: string;
  description: string;
  author: string;
  publisher: string;
}

let typescript: Book = {
  title: "알잘딱깔센 TypeScript",
  description: "타입스크립트 입문자가 읽으면 좋은 책",
}; // 불가능
interface Book {
  title?: string;
  description?: string;
  author?: string;
  publisher?: string;
}

let typescript: Partial<Book> = {
  title: "알잘딱깔센 TypeScript",
  description: "타입스크립트 입문자가 읽으면 좋은 책",
}; // 가능
interface Book {
  title: string;
  description: string;
  author?: string;
  publisher?: string;
}

let typescript: Book = {
  title: "알잘딱깔센 TypeScript",
  description: "타입스크립트 입문자가 읽으면 좋은 책",
}; // 가능
interface Book {
  title: string;
  description: string;
  author: string;
  publisher: string;
}

let typescript: Required<Book> = {
  title: "알잘딱깔센 TypeScript",
  description: "타입스크립트 입문자가 읽으면 좋은 책",
}; // 불가능
interface Book {
  title: string;
  description: string;
  author?: string;
  publisher?: string;
}

let typescript: Book = {
  title: "알잘딱깔센 TypeScript",
  description: "타입스크립트 입문자가 읽으면 좋은 책",
};

typescript.title = "TypeScript Basic"; // 재할당 가능
interface Book {
  readonly title: string;
  readonly description: string;
  readonly author?: string;
  readonly publisher?: string;
}

let typescript: Readonly<Book> = {
  title: "알잘딱깔센 TypeScript",
  description: "타입스크립트 입문자가 읽으면 좋은 책",
};

typescript.title = "TypeScript Basic"; // 재할당 불가능
interface Food {
  "1팀": "피자" | "치킨" | "햄버거" | "컵라면";
  "2팀": "피자" | "치킨" | "햄버거" | "컵라면";
  "3팀": "피자" | "치킨" | "햄버거" | "컵라면";
  "4팀": "피자" | "치킨" | "햄버거" | "컵라면";
}

const food: Food = {
  "1팀": "피자",
  "2팀": "치킨",
  "3팀": "햄버거",
  "4팀": "컵라면",
};
const food: Record<
  "1팀" | "2팀" | "3팀" | "4팀",
  "피자" | "치킨" | "햄버거" | "컵라면"
> = {
  "1팀": "피자",
  "2팀": "치킨",
  "3팀": "햄버거",
  "4팀": "컵라면",
};
type Team = "1팀" | "2팀" | "3팀" | "4팀";
type Food = "피자" | "치킨" | "햄버거" | "컵라면";

const food: Record<Team, Food> = {
  "1팀": "피자",
  "2팀": "치킨",
  "3팀": "햄버거",
  "4팀": "컵라면",
};
interface Person {
  name: string;
  age: number;
  location: string;
  gender: "M" | "W";
}

const kim: Pick<Person, "name" | "age"> = {
  name: "Kim",
  age: 27,
};
interface Person {
  name: string;
  age: number;
  location: string;
  gender: "M" | "W";
}

const kim: Pick<Person, "name" | "age"> = {
  name: "Kim",
  location: "Jeju", // Error: Type '{ name: string; location: string; }' is not assignable to type 'Pick<Person, "name" | "age">'
interface Person {
  name: string;
  age: number;
  location: string;
  gender: "M" | "W";
}

type Kim = Pick<Person, "name" | "age">;

const kim: Kim = {
  name: "Kim",
  age: 27,
};
interface Person {
  name: string;
  age: number;
  location: string;
  gender: "M" | "W";
}

const kim: Omit<Person, "age" | "gender"> = {
  name: "Kim",
  location: "Jeju",
};
interface Person {
  name: string;
  age: number;
  location: string;
  gender: "M" | "W";
}

const kim: Omit<Person, "age" | "gender"> = {
  name: "Kim",
  location: "Jeju",
  gender: "M", // Error: Type '{ name: string; location: string; gender: string; }' is not assignable to type 'Omit<Person, "age" | "gender">'
};
interface Person {
  name: string;
  age: number;
  location: string;
  gender: "M" | "W";
}

type Kim = Omit<Person, "age" | "gender">;

const kim: Kim = {
  name: "Kim",
  location: "Jeju",
};
type T1 = Exclude<"kim" | "lee" | "park", "park">;
// result : type T1 = "kim" | "lee"

type T2 = Exclude<string | number | boolean, number | boolean>;
// result : type T2 = string
type T1 = Exclude<string | number | boolean, number>;
// result : type T1 = string | boolean

type T2 = Exclude<T1, boolean>;
// result : type T2 = string
type T1 = NonNullable<string | number | boolean | null | undefined>;
// result : type T1 = string | number | boolean